package com.mh.service.impl;

import com.mh.common.ResponseResult;
import com.mh.controller.CaptchaController;
import com.mh.entity.LoginBody;
import com.mh.entity.LoginUser;
import com.mh.entity.SysUser;
import com.mh.exception.CaptchaException;
import com.mh.service.LoginService;
import com.mh.utils.JwtUtil;
import com.mh.utils.MyRedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Objects;

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenM;
    @Autowired
    private MyRedisCache redisCache;

    //用户登录逻辑
    @Override
    public ResponseResult login(SysUser user) {

        //1.使用AuthenticationManager的authenticate方法进行用户认证
        //1.1 封装用户名和密码: Authentication是接口,用实现类的子类UsernamePasswordAuthenticationToken创建对象
        Authentication token = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        /** 1.2 authenticate()方法需要传入Authentication接口对象,因此在上面创建对象
         *  authenticate用于手动触发认证的方法
         * 如果认证成功，返回一个已认证的 Authentication 对象
         * 如果认证失败，抛出 AuthenticationException 异常
         */
        Authentication authentication = authenM.authenticate(token);

        //2.如果认证失败,错误提示
        if(authentication == null){
            throw new RuntimeException("登录失败");
        }
        
        //3.如果认证成功,使用userId生成JWT,并将其保存到ResponseResult中返回
        //3.1 获取经过身份验证的用户主体信息
        //当用户已认证时，getPrincipal() 通常返回一个UserDetails接口对象
        //自定义的LoginUser类实现了UserDetails接口-->因此接口强转为实现类LoginUser
        LoginUser loginUser = (LoginUser)authentication.getPrincipal();

        //3.2 获取UserId 生成JWT
        String uid = loginUser.getUser().getUserId().toString();
        String jwt = JwtUtil.createJWT(uid);

        //4.将用户信息存储到Redis,在下一次请求时能够识别出用-->uid为key,loginUser为value
        redisCache.setCacheObject("login:" + uid, loginUser);

        //5.1 把token(jwt)封装到ResponseResult
        HashMap<String,String> map = new HashMap<>();
        map.put("token",jwt);
        //5.2 生成的token(jwt)返回给前端
        return new ResponseResult(200,"登录成功",map);
    }

    //用户退出逻辑-->删除Redis
    @Override
    public ResponseResult logout() {

        //在MyJWtAuthFilter类中用户信息(loginUser)存储到UsernamePasswordAuthenticationToken中
        //并放入到SecurityContextHolder中
        UsernamePasswordAuthenticationToken authToken =
                (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();

        //判断: 用户认证信息是否为空-->空抛异常
        if(Objects.isNull(authToken)){
            throw new RuntimeException("获取用户认证信息失败");
        }

        //非空继续获取用户对象(如UserDetails或自定义的LoginUser)
        LoginUser loginUser = (LoginUser)authToken.getPrincipal();
        Long uid = loginUser.getUser().getUserId();

        //删除redis中对应的用户信息
        redisCache.deleteObject("login:" + uid);

        return new ResponseResult(200,"注销成功");
    }

    @Override
    public ResponseResult captchaLogin(LoginBody loginBody) {

        //1. redis中获取验证码Captcha
        String key = CaptchaController.CAPTCHA_KEY + loginBody.getUuid();
        //用存储的key值获取value值(验证码captcha)
        Object captcha = redisCache.getCacheObject(key);

        //2. 获取value后在redis中删除该数据
        redisCache.deleteObject(key);

        //3. 判断: redis查询的验证码为空 或者 传入的验证码与redis的不一致-->抛异常
        if (captcha == null || loginBody.getCaptcha().equals(captcha)) {
            throw new CaptchaException("验证码错误");
        }

        //与上面的Login方法一致
        //4. 使用AuthenticationManager的authenticate方法进行用户认证
        Authentication authentication = authenM.authenticate
                (new UsernamePasswordAuthenticationToken(loginBody.getUsername(), loginBody.getPassword()));

        //5. 如果认证成功,使用userId生成JWT,并将其保存到ResponseResult中返回
        LoginUser loginUser = (LoginUser)authentication.getPrincipal();
        String uid = loginUser.getUser().getUserId().toString();
        String jwt = JwtUtil.createJWT(uid);

        //6.将用户信息存储到Redis,在下一次请求时能够识别出用-->uid为key,loginUser为value
        redisCache.setCacheObject("login:" + uid, loginUser);

        //7 把token(jwt)封装到ResponseResult
        HashMap<String,String> map = new HashMap<>();
        map.put("token",jwt);

        //8 生成的token(jwt)返回给前端
        return new ResponseResult(200,"登录成功",map);
    }
}
